#include "i2c.h"

static uint8_t i2c_read_register(struct i2c* pi2c, uint8_t slave_dev, uint16_t addr, uint16_t* data, uint8_t data_width)
{
    int32_t ret = 0;
    struct i2c_rdwr_ioctl_data rdwr = {0};
    struct i2c_msg msgs[2];
    uint16_t read_buf = 0;
    uint16_t write_buf = 0;

	msgs[0].len = data_width;
	msgs[0].addr = slave_dev; //slave device addr
	msgs[0].flags = 0;  //read or write flags
	msgs[0].buf = (uint8_t*)&write_buf;
	msgs[0].buf[0] = addr;     //slave device register 
    if(data_width == 1){
		msgs[0].buf[0] = addr & 0xff;     //slave device register 
	}else if(data_width == 2){
		msgs[0].buf[0] = addr >> 8;
		msgs[0].buf[1] = addr & 0xff;
	}else{
		printf("cannot support %d bit data\n", data_width * 8);
		return -1;
	}
	
	msgs[1].len = data_width;
	msgs[1].addr = slave_dev; //slave device addr
	msgs[1].flags = 1;  //read or write flags
	msgs[1].buf = (uint8_t*)&read_buf;

    rdwr.msgs = msgs;
    rdwr.nmsgs = 2;

    ret = ioctl(pi2c->fd, I2C_RDWR, &rdwr);
	if(ret < 0){
        DBG(DBG_ERR, "I2C read error\n");
        return -1;
    } else if(ret < rdwr.nmsgs){
		DBG(DBG_ERR, "Warning: only %d/%d messages were sent\n", ret, rdwr.nmsgs);
        return -1;
	}
    *data = read_buf;

    return read_buf;
}

static int32_t i2c_write_register(struct i2c* pi2c, uint8_t slave_dev, uint16_t addr, uint16_t data, uint8_t data_width)
{
	int32_t ret = 0;
    struct i2c_rdwr_ioctl_data rdwr = {0};
    struct i2c_msg msgs;
    uint16_t write_buf = 0;
    
    rdwr.msgs = &msgs;
    rdwr.nmsgs = 1;
	rdwr.msgs[0].len = 2;
	rdwr.msgs[0].addr = slave_dev; //slave device addr
	rdwr.msgs[0].flags = 0;  //read or write flags
	rdwr.msgs[0].buf = (uint8_t*)&write_buf;
	rdwr.msgs[0].buf[0] = addr;     //slave device register 
	rdwr.msgs[0].buf[1] = data;     //data 
	
	ret = ioctl(pi2c->fd, I2C_RDWR, &rdwr);
	if(ret < 0){
        DBG(DBG_ERR, "I2C read error\n");
        return -1;
    } else if(ret < rdwr.nmsgs){
		DBG(DBG_ERR, "Warning: only %d/%d messages were sent\n", ret, rdwr.nmsgs);
        return -1;
	}

	return 0;
}

static int32_t i2c_open(struct i2c* pi2c, char *dev)
{
    int32_t ret = 0;
    
    pi2c->fd = open(dev, O_RDWR);
	if (pi2c->fd < 0) {
		DBG(DBG_ERR, "Open %s failed.\n", dev);
		return -1;
	}
	
	// pi2c->slave_dev = slave_dev;
    pi2c->opened = 1;
    
    return ret;
}

static int32_t i2c_close(struct i2c* pi2c)
{
    int32_t ret = 0;

    if(!pi2c->opened){
        return 0;
    }
    
    close(pi2c->fd);
    pi2c->opened = 0;
    
    return ret;
}

static int32_t i2c_init(struct i2c* pi2c)
{
    int32_t ret = 0;

    INIT_LIST_HEAD(&(pi2c->head));
    lock_init(&(pi2c->lock));
    lock_init(&(pi2c->notify_lock));
    wait_init(&(pi2c->wait));

    /*
    create all sources
    ......
    */
    
    return ret;
}

static int32_t i2c_release(struct i2c* pi2c)
{
    /*
    free all sources
    ......
    */

    lock_destroy((&pi2c->notify_lock));
    lock_destroy((&pi2c->lock));
    wait_destroy((&pi2c->wait));

    return 0;
}

static int32_t i2c_register_notify(struct i2c* pi2c, int32_t event, i2c_event_notify notify, void* object)
{
    struct i2c_event_action* paction;
    if(!notify || (event <= I2C_EVENT_NONE) || (event >= I2C_EVENT_MAX)){
        return -EINVAL;
    }
    paction = (struct i2c_event_action*)malloc(sizeof(struct i2c_event_action));
    if(!paction){
        DBG(DBG_ERR,"malloc error\n");
        return -ENOMEM;
    }
    paction->notify = notify;
    paction->object = object;
    lock(&(pi2c->notify_lock));
    paction->next = pi2c->paction[event];
    pi2c->paction[event] = paction;
    unlock(&(pi2c->notify_lock));
    return 0;
}

static int32_t i2c_unregister_notify(struct i2c* pi2c, int32_t event, void* object)
{
    struct i2c_event_action *paction,* ptmp;
    if((event <= I2C_EVENT_NONE) || (event >= I2C_EVENT_MAX)){
        return -EINVAL;
    }
    lock(&(pi2c->notify_lock));
    paction = pi2c->paction[event];
    if(paction){
        if(paction->object == object){
            pi2c->paction[event] = paction->next;
            free(paction);
        }else{
            while(paction->next){
                if(paction->next->object == object){
                    ptmp = paction->next;
                    paction->next = ptmp->next;
                    free(ptmp);
                    break;
                }
                paction = paction->next;
            }
        }
    }
    unlock(&(pi2c->notify_lock));

    return 0;
}

static int32_t i2c_trigger_notify(struct i2c* pi2c, int32_t event, void* context)
{
    struct i2c_event_action* paction;
    if((event <= I2C_EVENT_NONE) || (event >= I2C_EVENT_MAX)){
        return -EINVAL;
    }
    lock(&(pi2c->notify_lock));
    paction = pi2c->paction[event];
    while(paction){
        paction->notify(pi2c, event, paction->object, context);
        paction = paction->next;
    }
    unlock(&(pi2c->notify_lock));

    return 0;
}

static struct i2c_operation i2c_op =
{
    .init = i2c_init,
    .release = i2c_release,
    
    .open = i2c_open,
    .close = i2c_close,
    
    .read = i2c_read_register,
    .write = i2c_write_register,

    .register_notify = i2c_register_notify,
    .unregister_notify = i2c_unregister_notify,
    .trigger_notify = i2c_trigger_notify,
};

int32_t create_init_i2c(struct i2c** pi2c)
{
    int32_t ret;
    struct i2c* ptmp;
    (*pi2c) = (struct i2c*)malloc(sizeof(struct i2c));
    if(!(*pi2c)){
        DBG(DBG_ERR,"malloc error\n");
        return -ENOMEM;
    }
    ptmp = *pi2c;
    memset(ptmp, 0, sizeof(struct i2c));
    ptmp->op = &i2c_op;
    ret = ptmp->op->init(ptmp);
    if(ret < 0){
        DBG(DBG_ERR,"init error\n");
        release_destroy_i2c(ptmp);
        return ret;
    }
    return 0;
}

void release_destroy_i2c(struct i2c* pi2c)
{
    if(pi2c){
        pi2c->op->release(pi2c);
        free(pi2c);
    }
}
        
